import { Directive, ElementRef, HostListener, Input } from '@angular/core';
import {
  loadingImgBase64 as defaultImgBase64,
  errorImgBase64,
} from './assets/image';
interface IConfig {
  // 指定根(root)元素，用于检查目标的可见性。必须是目标元素的父级元素。如果未指定或者为null，则默认为浏览器视窗。
  root: any;
  // 根(root)元素的外边距。类似于 CSS 中的  margin 属性，比如 "10px 20px 30px 40px" (top, right, bottom, left)。默认为0。
  rootMargin: string;
  // 可以是单一的number也可以是number数组，如果你想要target元素在root元素的可见程度每多25%就执行一次回调，那么你可以指定一个数组[0, 0.25, 0.5, 0.75, 1]。
  threshold: number | number[];
}

@Directive({
  selector: '[lazyloadSrc]',
})
export class LazyloadSrcDirective {
  // 通过Input(value)获取属性值,value命名该指令的属性（内部真实属性名），并且可给它指定一个用于绑定的别名；
  // 后面的参数 是外面用的属性别名（外部，你绑定到它地方，属性就叫这个）
  // 注意，像我们这里相同的其实里面参数可以不写省略
  @Input('lazyloadSrc') lazyloadSrc: string;
  // 加载失败资源属性
  @Input() defaultSrc: string;
  // 默认占位图属性
  @Input() errorSrc: string;
  // 支持手动设置是否开启Observer
  @Input() offObserver: boolean;
  // 加载失败事件监听
  @HostListener('error', ['$event']) onerror(event) {
    console.log('监听加载失败了',event)
    this.el.nativeElement.src = this.errorSrc || errorImgBase64;
    if (this.intersectionObserverInstance)
      this.stopIntersectionObserver(this.intersectionObserverInstance);
    if(this.throttleFnHandler) this.removeThrottleFn(this.throttleFnHandler);
  }
  private intersectionObserverInstance: IntersectionObserver;
  private throttleFnHandler: Function;
  // 在指令的构造函数中使用 ElementRef 来注入宿主 DOM 元素的引用，也就是你放置 lazyloadSrc属性 的那个元素。
  constructor(public el: ElementRef) {}
  // 为了确保能获取到数据，网页初始化完毕才调用
  ngOnInit(): void {
    this.init();
  }
  init() {
    console.log(this.offObserver);
    if (this.supportLazy()) this.el.nativeElement.loading = 'lazy';
    this.el.nativeElement.src = this.defaultSrc || defaultImgBase64;
    if (!this.offObserver && this.supportIntersectionObserver()) {
      console.log(1);
      this.initObserver();
    } else {
      console.log(2);
      this.initInterval();
    }
  }
  initObserver() {
    // if(!this.el.nativeElement.alt) this.el.nativeElement.alt = '该图片地址加载失败：' + this.lazyloadSrc
    this.intersectionObserverInstance = this.createIntersectionObserver(
      this.handleCallback
    );
    this.startIntersectionObserver(this.intersectionObserverInstance);
  }
  handleCallback(entries: any[]) {
    entries.forEach((item) => {
      // isIntersecting是一个Boolean值，判断目标元素当前是否可见
      if (item.isIntersecting) {
        let that: any = this;
        item.target.src = item.target.attributes.lazyloadsrc.value;
        // 图片加载后即停止监听该元素
        // // 命令IntersectionObserver停止对一个元素的观察
        // this.intersectionObserverInstance.unobserve(this.el.nativeElement);
        // 使IntersectionObserver整个对象停止监听工作。
        that.disconnect();
      }
    });
  }
  // 是否支持IntersectionObserver特性
  supportIntersectionObserver(): boolean {
    return 'IntersectionObserver' in window;
  }
  // img标签是否支持loding属性，如果有能设置lazy原生懒加载
  supportLazy() {
    // <img loading="lazy" src=""></img>
    let isSupport = 'loading' in document.createElement('img');
    return isSupport;
  }
  // 创建监听对象实例，传入监听成功时执行的回调函数 与 创建的配置
  createIntersectionObserver(
    callback: IntersectionObserverCallback = (
      entries: IntersectionObserverEntry[]
    ) => {},
    config: IConfig = {
      root: null,
      rootMargin: '0px',
      threshold: 0,
    }
  ) {
    return new IntersectionObserver(callback, config);
  }
  // 开启监测
  startIntersectionObserver(
    intersectionObserverInstance: IntersectionObserver
  ): void {
    intersectionObserverInstance.observe(this.el.nativeElement);
  }
  // 停止监测
  stopIntersectionObserver(
    intersectionObserverInstance: IntersectionObserver
  ): void {
    // 命令IntersectionObserver停止对一个元素的观察
    intersectionObserverInstance.unobserve(this.el.nativeElement);
    // 使IntersectionObserver整个对象停止监听工作。
    intersectionObserverInstance.disconnect();
    intersectionObserverInstance = null;
  }
  initInterval() {
    let _throttleFn = this.throttle(this.inViewShow);
    this.throttleFnHandler = _throttleFn;
    this.inViewShow(_throttleFn);
    console.log('initInterval被调用',this)
    document.addEventListener('scroll', _throttleFn.bind(this));
  }
  inViewShow(_throttleFn) {
    console.log('inViewShow被调用',this)
    const rect = this.el.nativeElement.getBoundingClientRect();
    // 出现在视野的时候加载图片
    if (rect.top < document.documentElement.clientHeight) {
      this.el.nativeElement.src = this.lazyloadSrc || defaultImgBase64;
      // 如果加载完 则去掉滚动事件监听
      this.removeThrottleFn(_throttleFn);
    }
  }
  removeThrottleFn(_throttleFn: any) {
    console.log('removeThrottleFn:',this)
    document.removeEventListener('scroll', _throttleFn.bind(this));
    _throttleFn = null;
    console.log('关闭监听：', this.el.nativeElement.src);
  }
  throttle(fn, delay = 15, mustRun = 30) {
    let t_start = null;
    let timer = null;
    let context = this;
    return function () {
      let t_current = +new Date();
      let args = Array.prototype.slice.call(arguments);
      clearTimeout(timer);
      if (!t_start) {
        t_start = t_current;
      }
      if (t_current - t_start > mustRun) {
        fn.apply(context, args);
        t_start = t_current;
      } else {
        timer = setTimeout(() => {
          fn.apply(context, args);
        }, delay);
      }
    };
  }
}
